/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package no.ffi.tpm.crypto;

import java.io.FileInputStream;
import java.io.Serializable;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.security.spec.MGF1ParameterSpec;
import java.util.Enumeration;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
//import javax.trustedcomputing.TrustedComputingException;
import javax.security.auth.x500.X500Principal;
import no.ffi.gismoidm.certstore.CertificateFileOperations;
import no.ffi.gismoidm.idp.Config;
import no.ffi.tpm.utils.POJOIdmException;

/**
 *
 * @author anders
 */
public class JCECryptoOperations extends CryptoOperations {
    
    
    private X509Certificate myCertificate, myTrustAnchorCert;
    private String myDistinguishedName; 

    public JCECryptoOperations() {
        initialize("");
    }
    
    public JCECryptoOperations(String appPrefix) {
        if (!appPrefix.endsWith("_")) 
            appPrefix = appPrefix + "_";
        initialize(appPrefix);
    }
    

    private boolean initialize(String appPrefix) {
        // Find own certificate and private key
        Config.loadPropertiesFrom(null);
        String keystoreFilename = Config.getProperty(appPrefix + "KEYSTORE_FILENAME");
        String keystorePassword = Config.getProperty(appPrefix + "KEYSTORE_PASSWORD");
        String trustAnchorPemFilename = Config.getProperty(appPrefix + "TRUSTANCHOR_FILENAME");
        try {
            // Load keystore and find private key
            KeyStore ks = KeyStore.getInstance("pkcs12");
            FileInputStream fis = new FileInputStream(keystoreFilename);
            ks.load(fis,keystorePassword.toCharArray());
            fis.close();
            // Only on entry in that file, so get the first alias
            Enumeration en = ks.aliases();
            if (en.hasMoreElements()) {
               String keystoreAlias = (String)en.nextElement();
                myPrivKey = (PrivateKey)ks.getKey(keystoreAlias, keystorePassword.toCharArray());
                myCertificate = ((X509Certificate)ks.getCertificate(keystoreAlias));
                myDistinguishedName = myCertificate.getSubjectX500Principal().getName();
            } else
               throw new POJOIdmException("Invalid PKCS12 file: " + keystoreFilename);
            if (myPrivKey==null) throw new POJOIdmException("No private key was found");


            //X509Certificate myCert = (X509Certificate)ks.getCertificate("user1");

            // Load trust anchor
            myTrustAnchorCert = CertificateFileOperations.loadPemCert(trustAnchorPemFilename);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    
   
    @Override
    //This does not return a deserialized object, but simply a byte[] (which is serializable
    //so we don't get an error) The deserialization must be done separately
    public byte[] decrypt(EncryptedObject eo) throws Exception {
        // First decrypt the secret key
        String mode=eo.algId;
        Cipher cip = Cipher.getInstance(mode);
        if(mode.equals(CryptoOperations.asymmetricAlgTPM)){
            OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA1", "MGF1", new MGF1ParameterSpec("SHA1"), new PSource.PSpecified("TCPA".getBytes("ASCII")));
            cip.init(Cipher.DECRYPT_MODE, getMyPrivKey(),oaepSpec);
        }
        if(mode.equalsIgnoreCase(asymmetricAlgPCA)){
            cip.init(Cipher.DECRYPT_MODE, getMyPrivKey());
        }
        byte[] secretkey =  cip.doFinal(eo.encryptedKey);
        //If the secret key was ecnrypted as a TpmSymmKey then there is some 
        //extra informatio that must be removed. This happens for example when 
        //an AIK request is sent to the PCA
        int preamble=eo.sessKeyPrefix;
        byte[] actualKey=new byte[secretkey.length-preamble];
        System.arraycopy(secretkey, preamble, actualKey, 0, actualKey.length);
        secretkey=actualKey;
        
        // Make a symmetric key and decrypt buffer
        SecretKey sessKey = new SecretKeySpec(secretkey,CryptoOperations.AES);

        // Make the secret key available for caller through the parameter object
        eo.secretKey = sessKey;
        
        //cip = Cipher.getInstance(symmetricAlg);
        //cip.init(Cipher.DECRYPT_MODE,sessKey);
        //cip.doFinal(eo.encryptedBuffer);
        return decrypt(eo.encryptedBuffer,sessKey);//deserialize(plaintext);
    }

     @Override
    public SignedObject sign(Serializable object) throws Exception {
        byte[] buffer;
        if (object instanceof byte[])
            buffer = (byte[])object;
        else
            buffer = serialize(object);
        Signature s = Signature.getInstance(signatureAlgorithm);
        s.initSign(myPrivKey);
        s.update(buffer);
        byte[] signature = s.sign();
        return new SignedObject(buffer,signature);
    }

   
   

    @Override
    public X509Certificate certifyPublicKey() {
        return myCertificate;
    }

       
    @Override
    public PublicKey getPubKey() {
        return this.myPubKey;
    }
    
    
    // Test method
    public static void main(String[] args) throws Exception {
        JCECryptoOperations co = new JCECryptoOperations();
        //co.initialize();

//        KeyPairGenerator kpg = KeyPairGenerator.getInstance("rsa");
//        kpg.initialize(512); // Parameter does not make a difference
//        KeyPair kp = kpg.generateKeyPair();
//        co.myPrivKey = kp.getPrivate();
        
        
        String plaintext = "abcdef123456789";
        EncryptedObject eo = co.encrypt(plaintext, co.myCertificate.getPublicKey(),asymmetricAlgTPM);
        String decrypted = (String)deserialize(co.decrypt(eo));
        System.out.println("Decrypted object = " + decrypted);
        
        String toBeSigned = "abcdef15555523456789";
        SignedObject so = co.sign(toBeSigned);
        
        // Verify signature
        System.out.println(co.verify(so,co.myCertificate.getPublicKey()));
        
    }

    /**
     * @return the myPrivKey
     */
    public PrivateKey getMyPrivKey() {
        return myPrivKey;
    }

    /*@Override
    public Serializable getISrequest(String pass,byte[] nonce) {
        throw new UnsupportedOperationException("Not supported yet.");
    }*/



 

    @Override
    public X509Certificate getTrustAnchorCert() {
        return myTrustAnchorCert;
    }

    @Override
    public PublicKey getTrustAnchor() {
        return myTrustAnchorCert.getPublicKey();
    }

    @Override
    public X500Principal getSubjectPrincipal() {
        return myCertificate.getSubjectX500Principal();
    }

    @Override
    public Serializable getISrequest() {
        return myDistinguishedName;
    }
    
    public String getMyDistinguishedName() {
        return myDistinguishedName;
    }


    
}